[Tips] 親子レコード対応版: SOQLの結果からId型のリストを抽出する
[Tips] 子レコード対応版: SOQLの結果からId型のリストを抽出する をさらに改良して、親レコードのId項目も抽出できるようにしました。
public class FieldSlice { private list<Object> olist; public FieldSlice(list<Object> olist) { this.olist = olist; } /** * 指定したIdフィールドのリストを返す * @param idFieldName Idフィールド名(親のリレーション A__r.B__c の形式も受容する) * @return 指定したIdフィールドのリスト */ public List<Id> retrieveIds(String idFieldName) { return retrieveIds(idFieldName, null); } /** * 指定したIdフィールドのリストを返す * @param idFieldName Idフィールド名(親のリレーション A__r.B__c の形式も受容する) * @param childRelationName 子リレーションから取得する場合に指定する子リレーション名 * @return 指定したIdフィールドのリスト */ public List<Id> retrieveIds(String idFieldName, String childRelationName) { List<Id> ids = new List<Id>(); for ( Object obj : olist ) { String s = JSON.serialize(obj); Map<String,Object> m = (Map<String,Object>)JSON.deserializeUntyped(s); if ( String.isEmpty(childRelationName) ) { Id id = retrieveId(m, idFieldName); // nullは結果から除外 if ( id != null ) { ids.add(id); } } else { // 子リレーションから取得 Map<String,Object> childRelation = (Map<String, Object>)m.get(childRelationName); List<Object> children = (List<Object>)childRelation.get('records'); ids.addAll(new FieldSlice(children).retrieveIds(idFieldName)); } } return new List<Id>(new Set<Id>(ids)); } /** * 指定したIdフィールドを返す * @param m 取得対象のObjectのMap * @param idFieldName Idフィールド名(親のリレーション A__r.B__c の形式も受容する) * @return 指定したIdフィールドのリスト */ private Id retrieveId(Map<String,Object> m, String idFieldName) { List<String> chainsOfField = idFieldName.split('\\.'); if ( chainsOfField.size() == 1 ) { return (Id)m.get(chainsOfField.get(0)); } m = (Map<String,Object>)m.get(chainsOfField.remove(0)); if ( m == null ) { return null; } return retrieveId(m, String.join(chainsOfField, '.')); } }
使い方は次の通り。
List<Account> opportunities = [ SELECT Id, Account.Id, Account.SomeParentObj__r.SomeAncestorObj__r.Id, ( SELECT Id FROM OpportunityLineItems ) FROM Opportunity ]; // ここで商談のIdを取得(従来の使い方でそのまま使える) List<Id> opportunityIds = new FieldSlice(opportunities).retrieveIds('Id'); // 子の商談商品のIdを取得(従来の使い方でそのまま使える) List<Id> oliIds = new FieldSlice(opportunities).retrieveIds('Id', 'OpportunityLineItems'); // 親の取引先のIdを取得 <= New! List<Id> accountIds = new FieldSlice(opportunities).retrieveIds('Account.Id'); // 商談の親(取引先)の親(SomeParentObj__c)の親(SomeAncestorObj__c)のIdを取得 <= New! List<Id> ancestorIds = new FieldSlice(opportunities).retrieveIds('Account.SomeParentObj__r.SomeAncestorObj__r.Id');
SOQLでは5世代までの親を遡れるので、FieldSlice.retrieveIds
でも5世代までは辿って取得できます。Apexにはガバナ制限により、SOQLの発行回数に制限があるため、一つのSOQLで親子構造を一気に取ってきておいて、FieldSlice
で個別にIdのリストを取得するようにすることでSOQL発行回数を節約できるメリットもあります。